package wsi.dataguide.tree;

import java.util.Stack;

import org.rygh.core.xml.AbstractXmlParser;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.gui.swing.input.Input;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import wsi.script.AppConfig;

public class WSXmlParser extends AbstractXmlParser {

	public static String checkValid(Input input, String xml) {
		if (xml == null || xml.trim().isEmpty())
			return null;

		WSXmlParser parser = new WSXmlParser(input, xml, true);

		if (!parser.isValid()) {
			System.out.println("FAILED. cannot be parsed: " + input.getValue());
			return null;
		}

		/*
		 * there should at least 3 nodes to be valid, a root node, a node
		 * containing the input value and other node containing more
		 * information. if there is no additional information, the tree can be
		 * invalid. node that attributesAsChildren should be enabled.
		 * 
		 * disabled as of 2013-11-12
		 */

		// WSXmlTree tree = parser.parse();
		//
		// if (tree.getConcreteRoot().getAllNodes().size() < 3) {
		// System.out.println("FAILED. too few nodes: " + input.getValue());
		// return null;
		// }

		return xml;
	}

	private final Stack<WSXmlLeaf> stack = new Stack<>();
	private final Stack<String> stackTag = new Stack<>();
	private final String xml;

	private final boolean attribAsChild;

	protected final Input input;
	protected final WSXmlTree xmlTree = new WSXmlTree();

	private WSXmlLeaf heldNode;

	public WSXmlParser(Input input, String xml, boolean attribAsChild) {
		this.input = input;
		this.xml = xml;
		this.attribAsChild = attribAsChild;

		xmlTree.setName(input.getValue());

		if (xml == null || xml.isEmpty())
			setValid(false);
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {

		String popName = stackTag.pop();

		if (heldNode == null) {
			String val = getString();

			if (!val.isEmpty()) {
				WSXmlLeaf leaf = stack.peek();

				if (leaf.isLeaf()) {
					WSXmlObject obj = leaf.getValue();
					obj.addValue(input, val);
				} else {
					String qname = XmlPath.SELF_TAG;

					WSXmlObject attnode = new WSXmlObject(qname);
					attnode.addValue(input, val);

					WSXmlLeaf _node = new WSXmlLeaf(leaf, attnode);
					_node.setXpath(new XmlPath(String.format("%s/%s", leaf
							.getXpath().getValue(), qname)));
				}

				assert (leaf.getValue().getName().equals(qName));
			}

			stack.pop();
		} else {
			if (stackTag.size() == stack.size() - 1) {
				WSXmlLeaf node = stack.pop();

				if (node.getName() != null)
					assert node.getName().equals(popName) : node.getName()
							+ ", " + popName;
			}
		}

		clearStringBuffer();
	}

	public WSXmlTree parse() {
		if (!isValid())
			return null;

		stackTag.push("root");
		stack.push(xmlTree.getRoot());

		UXml.parse(this, xml);

		return xmlTree;
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		stackTag.push(qName);

		WSXmlLeaf parent = stack.peek();

		if (!parent.isLeaf()
				&& parent.getChildren().size() > AppConfig.shared()
						.getDegreeLimit()) {
			heldNode = parent;
			return;
		}

		WSXmlObject node = new WSXmlObject(qName);
		WSXmlLeaf leaf = new WSXmlLeaf(parent, node);

		String prefix = "";

		if (parent.getValue() != null)
			prefix = String.format("%s/%s[%d]", parent.getXpath(), qName,
					parent.getIndex(leaf));
		else
			prefix = String.format("/%s[1]", qName);

		leaf.setXpath(new XmlPath(prefix));

		stack.push(leaf);
		heldNode = null;

		for (int i = 0; i < attributes.getLength(); i++) {
			if (attribAsChild) {
				String qname = attributes.getQName(i);
				String val = attributes.getValue(i);

				WSXmlObject attnode = new WSXmlObject(qname);
				attnode.setAttribute(true);
				attnode.addValue(input, val);

				WSXmlLeaf _node = new WSXmlLeaf(leaf, attnode);
				_node.setXpath(new XmlPath(String.format("%s/@%s", prefix,
						qname)));
			} else {
				node.addAttribute(attributes.getQName(i),
						attributes.getValue(i));
			}
		}

		clearStringBuffer();
	}

	@Override
	public void startPrefixMapping(String key, String val) throws SAXException {
		xmlTree.addPrefix(key, val);
	}

}
